package com.adv.ems.service.impl;

import com.adv.ems.db.entity.*;
import com.adv.ems.enums.SubjectTypeEnum;
import com.adv.ems.manager.CircuitDiagramNodeEnergySubjectRelationManager;
import com.adv.ems.manager.EnergyFeeInfoManager;
import com.adv.ems.manager.GatewayDeviceManager;
import com.adv.ems.manager.InstituteInfoManager;
import com.adv.ems.model.param.*;
import com.adv.ems.model.vo.*;
import com.adv.ems.service.BaseDataService;
import com.adv.ems.utils.EmsUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class BaseDataServiceImpl extends BaseServiceImpl implements BaseDataService {
    private final InstituteInfoManager instituteInfoManager;
    private final GatewayDeviceManager gatewayDeviceManager;
    private final CircuitDiagramNodeEnergySubjectRelationManager circuitDiagramNodeEnergySubjectRelationManager;
    private final EnergyFeeInfoManager energyFeeInfoManager;

    public BaseDataServiceImpl(InstituteInfoManager instituteInfoManager, GatewayDeviceManager gatewayDeviceManager, CircuitDiagramNodeEnergySubjectRelationManager circuitDiagramNodeEnergySubjectRelationManager, EnergyFeeInfoManager energyFeeInfoManager) {
        super();
        this.instituteInfoManager = instituteInfoManager;
        this.gatewayDeviceManager = gatewayDeviceManager;
        this.circuitDiagramNodeEnergySubjectRelationManager = circuitDiagramNodeEnergySubjectRelationManager;
        this.energyFeeInfoManager = energyFeeInfoManager;
    }

    @Override
    public EnergyBaseItemListVO validItemList(String acceptLanguage) {
        EnergyBaseItemListVO result = new EnergyBaseItemListVO();
        Set<ItemInfoDO> items = new HashSet<>();
        for (MeterDO m: meterManager.getAllMap().values()) {
            if (m.getItemId() == null) {
                continue;
            }
            ItemInfoDO itemInfoDO = itemInfoManager.getAllMap().get(m.getItemId());
            if (itemInfoDO == null) {
                continue;
            }
            items.add(itemInfoDO);
        }
        List<EnergyBaseItemListVO.Info> list = items
                .stream()
                .map(e -> new EnergyBaseItemListVO.Info(e, acceptLanguage))
                .collect(Collectors.toList());
        result.setList(list);
        return result;
    }

    @Override
    public IndexEnergyInfoVO energyList(String acceptLanguage) {
        Set<Long> energyIds = meterManager
                .getAllMap()
                .values()
                .stream()
                .map(MeterDO::getEnergyId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        List<IndexEnergyInfoVO.Info> list = energyInfoManager
                .getAllMap()
                .values()
                .stream()
                .filter(e -> energyIds.contains(e.getId()))
                .sorted(Comparator.comparing(EnergyInfoDO::getSort))
                .map(e -> new IndexEnergyInfoVO.Info(e, acceptLanguage))
                .collect(Collectors.toList());
        IndexEnergyInfoVO result = new IndexEnergyInfoVO();
        result.setList(list);
        return result;
    }

    @Override
    public BaseBuildingVO buildingList(BaseQueryParam param, String acceptLanguage) {
        BaseBuildingVO result = new BaseBuildingVO();
        Collection<BuildingInfoDO> values = buildingInfoManager.getAllMap().values();
        List<BaseBuildingVO.Info> collect = values.stream()
                .filter(b -> !StringUtils.hasLength(param.getKeyword()) || b.getNameInfo().contains(param.getKeyword()))
                .sorted(Comparator.comparing(BuildingInfoDO::getUpdateTime).reversed())
                .map(b -> {
                    BuildingTypeInfoDO buildingTypeInfo = null;
                    InstituteInfoDO instituteInfo = null;
                    if (b.getBuildingType() != null) {
                        buildingTypeInfo = buildingTypeInfoManager.getAllMap().values().stream().filter(t -> b.getBuildingType().equals(t.getCode())).findFirst().orElse(null);
                    }
                    if (b.getInstituteId() != null) {
                        instituteInfo = instituteInfoManager.getAllMap().get(b.getInstituteId());
                    }
                    return new BaseBuildingVO.Info(b, instituteInfo, buildingTypeInfo, acceptLanguage);
                })
                .collect(Collectors.toList());
        result.getList().addAll(collect);
        return result;
    }

    @Override
    public Boolean buildingUpdate(BaseBuildingVO.Info param, String acceptLanguage) {
        BuildingInfoDO info = new BuildingInfoDO();
        BeanUtils.copyProperties(param, info);
        info.setUpdateTime(new Date());
        if (info.getCreateTime() == null) {
            info.setCreateTime(new Date());
        }
        buildingInfoManager.saveOrUpdateData(info);
        return true;
    }

    @Override
    public BaseInstituteVO instituteList(BaseQueryParam param, String acceptLanguage) {
        BaseInstituteVO result = new BaseInstituteVO();
        List<BaseInstituteVO.Info> collect = instituteInfoManager
                .getAllMap()
                .values()
                .stream()
                .filter(b -> !StringUtils.hasLength(param.getKeyword()) || b.getNameInfo().contains(param.getKeyword()))
                .sorted(Comparator.comparing(InstituteInfoDO::getUpdateTime).reversed())
                .map(i -> {
                    InstituteInfoDO parent = null;
                    if (i.getParentId() != null) {
                        parent = instituteInfoManager.getAllMap().get(i.getParentId());
                    }
                    return new BaseInstituteVO.Info(i, parent, acceptLanguage);
                }).collect(Collectors.toList());
        result.getList().addAll(collect);
        return result;
    }

    @Override
    public Boolean instituteUpdate(BaseInstituteVO.Info param, String acceptLanguage) {
        InstituteInfoDO info = new InstituteInfoDO();
        BeanUtils.copyProperties(param, info);
        info.setUpdateTime(new Date());
        if (info.getCreateTime() == null) {
            info.setCreateTime(new Date());
        }
        instituteInfoManager.saveOrUpdateData(info);
        if (info.getPath() == null && info.getParentId() != null) {
            Map<Long, InstituteInfoDO> allMap = instituteInfoManager.getAllMap();
            StringBuilder path = new StringBuilder();
            EmsUtil.getAbsolutePath(info.getId(), path, allMap, InstituteInfoDO :: getParentId, InstituteInfoDO :: getPath);
            if (path.length() > 0) {
                info.setPath(path.toString());
                instituteInfoManager.saveOrUpdateData(info);
            }
        }
        return true;
    }

    @Override
    public BaseBuildingTypeVO buildingTypeList(BaseQueryParam param, String acceptLanguage) {
        BaseBuildingTypeVO result = new BaseBuildingTypeVO();
        List<BaseBuildingTypeVO.Info> collect = buildingTypeInfoManager.getAllMap().values().stream().map(BaseBuildingTypeVO.Info::new).collect(Collectors.toList());
        result.getList().addAll(collect);
        return result;
    }

    @Override
    public BaseDepartmentVO departmentList(BaseQueryParam param, String acceptLanguage) {
        BaseDepartmentVO result = new BaseDepartmentVO();
        Collection<DepartmentInfoDO> values = departmentInfoManager.getAllMap().values();
        List<BaseDepartmentVO.Info> collect = values.stream()
                .filter(b -> !StringUtils.hasLength(param.getKeyword()) || b.getNameInfo().contains(param.getKeyword()))
                .sorted(Comparator.comparing(DepartmentInfoDO::getUpdateTime).reversed())
                .map(d -> {
                    BuildingInfoDO buildingInfo = null;
                    if (d.getBuildingId() != null) {
                        buildingInfo = buildingInfoManager.getAllMap().get(d.getBuildingId());
                    }
                    return new BaseDepartmentVO.Info(d, buildingInfo, acceptLanguage);
                }).collect(Collectors.toList());
        result.getList().addAll(collect);
        return result;
    }

    @Override
    public Boolean departmentUpdate(BaseDepartmentVO.Info param, String acceptLanguage) {
        DepartmentInfoDO info = new DepartmentInfoDO();
        BeanUtils.copyProperties(param, info);
        info.setUpdateTime(new Date());
        if (info.getCreateTime() == null) {
            info.setCreateTime(new Date());
        }
        departmentInfoManager.saveOrUpdateData(info);
        return true;
    }

    @Override
    public BaseMeterVO meterList(BaseMeterParam param, String acceptLanguage) {
        BaseMeterVO result = new BaseMeterVO();
        Collection<MeterDO> values = meterManager.getAllMap().values();
        if (param.getSubjectId() != null && param.getSubjectType() != null) {
            List<Long> meterIds = getMeterIdsBySubjectId(param.getSubjectId(), param.getSubjectType());
            values = values.stream().filter(m -> meterIds.contains(m.getId())).collect(Collectors.toList());
        }
        List<BaseMeterVO.Info> collect = values.stream()
                .filter(b -> !StringUtils.hasLength(param.getKeyword()) || b.getName().contains(param.getKeyword()))
                .sorted(Comparator.comparing(MeterDO::getUpdateTime).reversed())
                .map(d -> {
                    InstituteInfoDO instituteInfo = null;
                    BuildingInfoDO buildingInfo = null;
                    EnergyInfoDO energyInfo = null;
                    ItemInfoDO itemInfo = null;
                    ItemOneInfoDO itemOneInfo = null;
                    ItemTwoInfoDO itemTwoInfo = null;
                    if (d.getInstituteId() != null) {
                        instituteInfo = instituteInfoManager.getAllMap().get(d.getInstituteId());
                    }
                    if (d.getBuildingId() != null) {
                        buildingInfo = buildingInfoManager.getAllMap().get(d.getBuildingId());
                    }
                    if(d.getEnergyId() != null) {
                        energyInfo = energyInfoManager.getAllMap().get(d.getEnergyId());
                    }
                    if (d.getItemId() != null) {
                        itemInfo = itemInfoManager.getAllMap().get(d.getItemId());
                    }
                    if (d.getItemOneId() != null) {
                        itemOneInfo = itemOneInfoManager.getAllMap().get(d.getItemOneId());
                    }
                    if (d.getItemTwoId() != null) {
                        itemTwoInfo = itemTwoInfoManager.getAllMap().get(d.getItemTwoId());
                    }
                    return new BaseMeterVO.Info(d, instituteInfo, buildingInfo, energyInfo, itemInfo, itemOneInfo, itemTwoInfo, acceptLanguage);
                }).collect(Collectors.toList());
        result.getList().addAll(collect);
        return result;
    }

    @Override
    public Boolean meterUpdate(BaseMeterVO.Info param, String acceptLanguage) {
        param.setUpdateTime(new Date());
        if (param.getCreateTime() == null) {
            param.setCreateTime(new Date());
        }
        meterManager.saveOrUpdateData(param);
        return true;
    }

    @Override
    public BasePointVO pointList(BasePointQueryParam param, String acceptLanguage) {
        BasePointVO result = new BasePointVO();
        Collection<CollectPointDO> values = collectPointManager.getAllMap().values();
        List<BasePointVO.Info> collect = values.stream()
                .sorted(Comparator.comparing(CollectPointDO::getUpdateTime).reversed())
                .map(d -> {
                    GatewayDeviceDO gatewayDevice = null;
                    MeterDO meter = null;
                    CollectPointFunctionDO functionDO = null;
                    if (d.getFunctionCode() != null) {
                        List<CollectPointFunctionDO> funcs = collectPointFunctionManager.getAllMap().values().stream().filter(f -> d.getFunctionCode().equals(d.getFunctionCode())).collect(Collectors.toList());
                        functionDO = !funcs.isEmpty() ? funcs.get(0) : null;
                    }
                    if (d.getGatewayId() != null) {
                        gatewayDevice = gatewayDeviceManager.getAllMap().get(d.getGatewayId());
                    }
                    if (d.getMeterId() != null) {
                        meter = meterManager.getAllMap().get(d.getMeterId());
                    }
                    return new BasePointVO.Info(d, functionDO, gatewayDevice, meter, acceptLanguage);
                })
                .filter(p -> param.getHealth() == null || param.getHealth().equals(p.getHealth()))
                .filter(p -> param.getVirtual() == null || param.getVirtual().equals(p.getVirtual()))
                .filter(p -> param.getDataType() == null || param.getDataType().equals(p.getDataType()))
                .filter(p -> param.getGatewayId() == null || param.getGatewayId().equals(p.getGatewayId()))
                .filter(p -> param.getMeterId() == null || param.getMeterId().equals(p.getMeterId()))
                .filter(b -> !StringUtils.hasLength(param.getKeyword()) || b.getName().contains(param.getKeyword()))
                .collect(Collectors.toList());
        result.setTotal(collect.size());
        int from = (param.getPageNo() - 1) * param.getPageSize();
        int to = from + param.getPageSize();
        if (collect.size() >= to) {
            result.getList().addAll(collect.subList(from, to));
        }else {
            result.getList().addAll(collect.subList(from, collect.size()));
        }
        return result;
    }

    @Override
    public Boolean pointUpdate(BasePointVO.Info param, String acceptLanguage) {
        param.setUpdateTime(new Date());
        if (param.getCreateTime() == null) {
            param.setCreateTime(new Date());
        }
        param.setLastUploadRealValue(null);
        param.setLastUploadTime(null);
        collectPointManager.saveOrUpdateData(param);
        return true;
    }

    @Override
    public BaseItemVO list(BaseQueryParam param, String acceptLanguage) {
        BaseItemVO result = new BaseItemVO();
        List<BaseItemVO.Info> collect = itemInfoManager
                .getAllMap()
                .values()
                .stream()
                .filter(b -> !StringUtils.hasLength(param.getKeyword()) || b.getNameInfo().contains(param.getKeyword()))
                .sorted(Comparator.comparing(ItemInfoDO::getSort))
                .map(o -> {
                    EnergyInfoDO energyInfo = energyInfoManager.getAllMap().get(o.getEnergyId());
                    return new BaseItemVO.Info(o, energyInfo, acceptLanguage);
                })
                .collect(Collectors.toList());
        result.getList().addAll(collect);
        return result;
    }

    @Override
    public Boolean diagramUpdate(BaseDiagramInfoParam info, String acceptLanguage) {
        info.setUpdateTime(new Date());
        if (info.getCreateTime() == null) {
            info.setCreateTime(new Date());
        }
        circuitDiagramNodeManager.saveOrUpdateData(info);
        if (info.getPath() == null && info.getParentId() != null) {
            Map<Long, CircuitDiagramNodeDO> allMap = circuitDiagramNodeManager.getAllMap();
            StringBuilder path = new StringBuilder();
            EmsUtil.getAbsolutePath(info.getId(), path, allMap, CircuitDiagramNodeDO :: getParentId, CircuitDiagramNodeDO :: getPath);
            if (path.length() > 0) {
                info.setPath(path.toString());
                circuitDiagramNodeManager.saveOrUpdateData(info);
            }
        }
        return true;
    }

    @Override
    public Boolean diagramDelete(Long id, String acceptLanguage) {
        circuitDiagramNodeManager.deleteByPath(id);
        return true;
    }

    @Override
    public BaseDiagramRelateListVO relateList(Long id, String acceptLanguage) {
        BaseDiagramRelateListVO result = new BaseDiagramRelateListVO();
        circuitDiagramNodeEnergySubjectRelationManager
                .getAllMap()
                .values()
                .stream()
                .filter(r -> r.getDiagramNodeId().equals(id))
                .forEach(r -> {
                    if (SubjectTypeEnum.BUILDING.name().equals(r.getType())) {
                        BuildingInfoDO buildingInfoDO = buildingInfoManager.getAllMap().get(r.getSubjectId());
                        if (buildingInfoDO != null) {
                            result.getSubjects().add(new BaseDiagramRelateListVO.Info(r.getId(), buildingInfoDO, acceptLanguage));
                        }
                    }
                    if (SubjectTypeEnum.DEPARTMENT.name().equals(r.getType())) {
                        DepartmentInfoDO departmentInfoDO = departmentInfoManager.getAllMap().get(r.getSubjectId());
                        if (departmentInfoDO != null) {
                            result.getSubjects().add(new BaseDiagramRelateListVO.Info(r.getId(), departmentInfoDO, acceptLanguage));
                        }
                    }
                });
        circuitDiagramNodeMeterRelationManager
                .getAllMap()
                .values()
                .stream()
                .filter(r -> r.getDiagramNodeId().equals(id))
                .forEach(r -> {
                    MeterDO meterDO = meterManager.getAllMap().get(r.getMeterId());
                    if (meterDO != null) {
                        result.getMeters().add(new BaseDiagramRelateListVO.Info(r.getId(), meterDO));
                    }
                });
        return result;
    }

    @Override
    public Boolean relateAdd(BaseDiagramRelateAddVO param, String acceptLanguage) {
        if (param.getSubjectType().equals(SubjectTypeEnum.BUILDING)
                || param.getSubjectType().equals(SubjectTypeEnum.DEPARTMENT)) {
            CircuitDiagramNodeEnergySubjectRelationDO relation = new CircuitDiagramNodeEnergySubjectRelationDO();
            relation.setCreateTime(new Date());
            relation.setUpdateTime(new Date());
            relation.setDiagramNodeId(param.getDiagramId());
            relation.setType(param.getSubjectType().name());
            relation.setSubjectId(param.getSubjectId());
            circuitDiagramNodeEnergySubjectRelationManager.saveData(relation);
        }
        if (param.getSubjectType().equals(SubjectTypeEnum.METER)) {
            CircuitDiagramNodeMeterRelationDO relation = new CircuitDiagramNodeMeterRelationDO();
            relation.setCreateTime(new Date());
            relation.setUpdateTime(new Date());
            relation.setDiagramNodeId(param.getDiagramId());
            relation.setMeterId(param.getSubjectId());
            circuitDiagramNodeMeterRelationManager.saveData(relation);
        }
        return true;
    }

    @Override
    public Boolean relateDelete(BaseDiagramRelateRelationDelVO param, String acceptLanguage) {
        if (param.getSubjectType().equals(SubjectTypeEnum.BUILDING)
                || param.getSubjectType().equals(SubjectTypeEnum.DEPARTMENT)) {
            circuitDiagramNodeEnergySubjectRelationManager.deleteById(param.getRelationId());
        }
        if (param.getSubjectType().equals(SubjectTypeEnum.METER)) {
            circuitDiagramNodeMeterRelationManager.deleteById(param.getRelationId());
        }
        return true;
    }

    @Override
    public Boolean energyFeeUpdate(BaseEnergyFeeVO.Info param, String acceptLanguage) {
        energyFeeInfoManager.saveOrUpdateData(param);
        return true;
    }

    @Override
    public BaseEnergyFeeVO energyFeeList(BaseQueryParam param, String acceptLanguage) {
        BaseEnergyFeeVO result = new BaseEnergyFeeVO();
        Map<Long, List<BaseEnergyFeeVO.Info>> collect = energyFeeInfoManager
                .getAllMap()
                .values()
                .stream()
                .sorted(Comparator.comparing(EnergyFeeInfoDO::getEnergyId))
                .filter(f -> energyInfoManager.getAllMap().get(f.getEnergyId()) != null)
                .filter(r -> StringUtils.isEmpty(param.getKeyword()) || r.getName().contains(param.getKeyword()))
                .map(f -> {
                    InstituteInfoDO instituteInfoDO = null;
                    BuildingInfoDO buildingInfo = null;
                    EnergyInfoDO energyInfoDO = energyInfoManager.getAllMap().get(f.getEnergyId());
                    if (f.getInstituteId() != null) {
                        instituteInfoDO = instituteInfoManager.getAllMap().get(f.getInstituteId());
                    }
                    if (f.getBuildingId() != null) {
                        buildingInfo = buildingInfoManager.getAllMap().get(f.getBuildingId());
                    }
                    return new BaseEnergyFeeVO.Info(f, instituteInfoDO, buildingInfo, energyInfoDO, acceptLanguage);
                }).collect(Collectors.groupingBy(BaseEnergyFeeVO.Info::getEnergyId));
        for (Map.Entry<Long, List<BaseEnergyFeeVO.Info>> entry : collect.entrySet()) {
            EnergyInfoDO energyInfoDO = energyInfoManager.getAllMap().get(entry.getKey());
            List<BaseEnergyFeeVO.Info> value = entry.getValue();
            result.getList().add(new BaseEnergyFeeVO.Item(energyInfoDO, value, acceptLanguage));
        }
        return result;
    }

    @Override
    public Boolean energyFeeDelete(Long id, String acceptLanguage) {
        energyFeeInfoManager.deleteById(id);
        return true;
    }

    @Override
    public BaseEnergyVO energyAllList(BaseQueryParam param, String acceptLanguage) {
        BaseEnergyVO result = new BaseEnergyVO();
        List<EnergyInfoDO> collect = energyInfoManager
                .getAllMap()
                .values()
                .stream()
                .sorted(Comparator.comparing(EnergyInfoDO::getSort))
                .filter(r -> StringUtils.isEmpty(param.getKeyword()) || r.getNameInfo().contains(param.getKeyword()))
                .collect(Collectors.toList());
        result.getList().addAll(collect);
        return result;
    }

    @Override
    public BaseOneItemVO oneList(BaseQueryParam param, String acceptLanguage) {
        BaseOneItemVO result = new BaseOneItemVO();
        Map<Long, ItemOneInfoDO> allMap = itemOneInfoManager.getAllMap();
        List<BaseOneItemVO.Info> collect = allMap.values()
                .stream()
                .filter(b -> !StringUtils.hasLength(param.getKeyword()) || b.getNameInfo().contains(param.getKeyword()))
                .sorted(Comparator.comparing(ItemOneInfoDO::getSort))
                .map(o -> new BaseOneItemVO.Info(o, acceptLanguage))
                .collect(Collectors.toList());
        result.getList().addAll(collect);
        return result;
    }

    @Override
    public BaseTwoItemVO twoList(BaseQueryParam param, String acceptLanguage) {
        BaseTwoItemVO result = new BaseTwoItemVO();
        Map<Long, ItemTwoInfoDO> allMap = itemTwoInfoManager.getAllMap();
        List<BaseTwoItemVO.Info> collect = allMap.values()
                .stream()
                .filter(b -> !StringUtils.hasLength(param.getKeyword()) || b.getNameInfo().contains(param.getKeyword()))
                .sorted(Comparator.comparing(ItemTwoInfoDO::getSort))
                .map(o -> new BaseTwoItemVO.Info(o, acceptLanguage))
                .collect(Collectors.toList());
        result.getList().addAll(collect);
        return result;
    }

    @Override
    public BaseGatewayVO gatewayList(BaseQueryParam param, String acceptLanguage) {
        BaseGatewayVO result = new BaseGatewayVO();
        Map<Long, GatewayDeviceDO> allMap = gatewayDeviceManager.getAllMap();
        List<BaseGatewayVO.Info> collect = allMap.values()
                .stream()
                .filter(b -> !StringUtils.hasLength(param.getKeyword()) || b.getName().contains(param.getKeyword()))
                .sorted(Comparator.comparing(GatewayDeviceDO::getUpdateTime).reversed())
                .map(o -> {
                    BuildingInfoDO buildingInfoDO = null;
                    if (o.getBuildingId() != null) {
                        buildingInfoDO = buildingInfoManager.getAllMap().get(o.getBuildingId());
                    }
                    return new BaseGatewayVO.Info(o, buildingInfoDO, acceptLanguage);
                })
                .collect(Collectors.toList());
        result.getList().addAll(collect);
        return result;
    }

    @Override
    public Boolean gatewayUpdate(BaseGatewayVO.Info param, String acceptLanguage) {
        param.setUpdateTime(new Date());
        if (param.getCreateTime() == null) {
            param.setCreateTime(new Date());
        }
        gatewayDeviceManager.saveOrUpdateData(param);
        return true;
    }
}
